/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "BSW_UART.h"
#include "device_w25q128.h"
#include "C_CRC16.h"
#include <string.h>
#include "stm32f4xx_hal_flash.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
uint8_t Gloable_u8MCU_BootSta = 0;
uint8_t Gloable_u8MCU_BootSta_last = 0;
 // MCU wake up into this status
#define BootSta_Init  		0
// @suppress("Line comments") 	When MCU operation Init over into this status
#define BootSta_StandBy  	1
// In StandBy status not over 3s and receive the UART message "StayBoot" will into this status
#define BootSta_StayBoot  	2
//In StandBy status over 3s and not receive the UART message "StayBoot" will into this status
#define BootSta_JumpAPP  	3
//When in StayBoot status and received a pocket BinDate will into this status form StayBoot
//When copy date to SPI Flash and Send a ATK to PC, will return to StayBoot status
#define BootSta_DealDate  	4
//When deal with all the BinDate,will into this status,and Copy all date to MCU Flash,and write this Boot log.In the last into JunmAPP status
#define BootSta_DealFlash  	5
uint16_t Gloable_u16BinDatePocketNum = 0;
uint8_t Gloable_u8FirstIntoStayBootFlag =0;

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* 开关全局中断的宏 */
#define ENABLE_INT() __set_PRIMASK(0)  /* 使能全局中断 */
#define DISABLE_INT() __set_PRIMASK(1) /* 禁止全局中断 */
void (*SysMemBootJump)(void);        /* 声明一个函数指针 */
static uint32_t AppAddr = MCU_FLASH_APP_StartAddress;    /* APP地址 */

void JumpToApp(void)
{


  /* 如果初始化了外设，需要反向初始化外设 */
  HAL_GPIO_DeInit(GPIOA,Blue_High_Pin|Green_High_Pin);
  HAL_GPIO_DeInit(SPI1_CS_GPIO_Port, SPI1_CS_Pin);
  HAL_UART_MspDeInit(&huart1);
  HAL_UART_MspDeInit(&huart3);
  HAL_SPI_MspDeInit(&hspi1);
  HAL_TIM_Base_MspDeInit(&htim6);
  HAL_TIM_Base_MspDeInit(&htim7);
  HAL_TIM_Base_MspDeInit(&htim8);
  /* 设置所有时钟到默认状态，使用HSI时钟 */
  HAL_RCC_DeInit();

  /* 关闭滴答定时器，复位到默认值 */
  SysTick->CTRL = 0;
  SysTick->LOAD = 0;
  SysTick->VAL = 0;

  /* 关闭全局中断 */
  DISABLE_INT();

  /* 关闭所有中断，清除所有中断挂起标志 */
  for (uint32_t i = 0; i < 8; i++)
  {
    NVIC->ICER[i]=0xFFFFFFFF;
    NVIC->ICPR[i]=0xFFFFFFFF;
  }

  /* 使能全局中断 */
  ENABLE_INT();

  /* 设置SysMemBootJump为中断服务函数入口地址，首地址是MSP，地址+4是复位中断服务程序地址 */
  SysMemBootJump = (void (*)(void)) (*((uint32_t *) (AppAddr + 4)));

  /* 设置主堆栈指针 */
  __set_MSP(*(uint32_t *)AppAddr);

  /* 在RTOS工程，这条语句很重要，设置为特权级模式，SP使用MSP指针 */
  __set_CONTROL(0);

  /* 进行跳转 也就是将SysMemBootJump 赋值给PC */
  SysMemBootJump();

}
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();
  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART3_UART_Init();
  MX_USART1_UART_Init();
  MX_SPI1_Init();
  MX_TIM6_Init();
  MX_TIM7_Init();
  MX_TIM8_Init();
  /* USER CODE BEGIN 2 */
  Gloable_u8MCU_BootSta = BootSta_Init;
  __HAL_TIM_CLEAR_IT(&htim7, TIM_IT_UPDATE);
  HAL_TIM_Base_Start_IT(&htim7);
  BSW_UART_Init();
  BSP_W25Qx_Init();
  Gloable_u8MCU_BootSta = BootSta_StandBy;
  Gloable_u8MCU_BootSta_last = BootSta_Init;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	switch (Gloable_u8MCU_BootSta)
	{
		case BootSta_StandBy:
			if (Gloable_u8MCU_BootSta_last != BootSta_StandBy)
			{
				// other status get into BootSta_StandBy should do this 
				my_printf("Into BootSta_StandBy\r\n");
				Gloable_u8MCU_BootSta_last = BootSta_StandBy;
				__HAL_TIM_CLEAR_IT(&htim8, TIM_IT_UPDATE);
				HAL_TIM_Base_Start_IT(&htim8);
			}

			break;
		case BootSta_StayBoot:
			if (Gloable_u8MCU_BootSta_last != BootSta_StayBoot)
			{
				// other status get into BootSta_StayBoot should do this 
				HAL_TIM_Base_Stop_IT(&htim8);
				my_printf("Into BootSta_StayBoot\r\n");
				Gloable_u8MCU_BootSta_last = BootSta_StayBoot;

				// first time from other status get into BootSta_StayBoot should do this 
				if (Gloable_u8FirstIntoStayBootFlag == 0)
				{
					Gloable_u8FirstIntoStayBootFlag ++;
					my_printf(">>>>>Waiting for Eraseing SPI Flash\r\n");
					for (uint8_t i=0;i<128;i++)
					{
						BSP_W25Qx_Erase_Block(i*4096);//each time erase 4096byte
						my_printf(">>>>>Erase SECTOR %d\r\n",i);
					}
					my_printf(">>>>>Eraseing SPI Flash Done\r\n");
				}
			}
			break;
		case BootSta_JumpAPP:
			if (Gloable_u8MCU_BootSta_last != BootSta_JumpAPP)
			{
				// other status get into BootSta_JumpAPP should do this 
				my_printf("Into BootSta_JumpAPP\r\n");
				Gloable_u8MCU_BootSta_last = BootSta_JumpAPP;
				HAL_TIM_Base_Stop_IT(&htim8);

				// show all date in the MCU Flash
				uint32_t app_startaddress = MCU_FLASH_APP_StartAddress;
				uint8_t MCU_APP_Flash_NoneFlag =1;

				if (*(volatile uint8_t*)app_startaddress == 0xFF && *(volatile uint8_t*)(app_startaddress+1) == 0xFF && *(volatile uint8_t*)(app_startaddress+254) == 0xFF && *(volatile uint8_t*)(app_startaddress+255) == 0xFF)
				{
					MCU_APP_Flash_NoneFlag=1;
				}
				else
				{
					MCU_APP_Flash_NoneFlag=0;
				}
				for (uint32_t i=0;i<256;i++)
				{
					uint8_t read_arr_256byte[256];
					for (uint16_t j=0;j<256;j++)
					{
						read_arr_256byte[j] = *(volatile uint8_t*)app_startaddress;
						app_startaddress++;
					}
					HAL_UART_Transmit(&huart3, (uint8_t *)&read_arr_256byte, 256,0xFFFF);
				}

				//Closed all NVIC inorder to protect the jump from BootLoad to APP

				//Junp to APP
				if (MCU_APP_Flash_NoneFlag == 0)
				{
					JumpToApp();
					my_printf(">>>>>JumpAPP ERR!!!\r\n");
				}
				else
				{
					my_printf(">>>>>JumpAPP ERR because MCU_APP_Flash_None!!!\r\n");
				}
				
			}


			break;
		case BootSta_DealDate:
			if (Gloable_u8MCU_BootSta_last != BootSta_DealDate)
			{
				// other status get into BootSta_DealDate should do this 
				my_printf("Into BootSta_DealDate\r\n");
				Gloable_u8MCU_BootSta_last = BootSta_DealDate;
				HAL_TIM_Base_Stop_IT(&htim8);

				// Copy receive data from uart to SPI Flash
				uint32_t deal_address = Gloable_u8MCU_TO_PC_UART_RxArr[Gloable_u32MCU_TO_PC_UART_RxLen-3]*256;
				BSP_W25Qx_Write(Gloable_u8MCU_TO_PC_UART_RxArr, deal_address, Gloable_u32MCU_TO_PC_UART_RxLen-3);

				// After copy date should go back to BootSta_StayBoot
				Gloable_u8MCU_BootSta = BootSta_StayBoot;
			}
			break;
		case BootSta_DealFlash:
			if (Gloable_u8MCU_BootSta_last != BootSta_DealFlash)
			{
				// other status get into BootSta_DealFlash should do this 
				my_printf("Into BootSta_DealFlash\r\n");
				my_printf("BinDate Pocket Num is: %d\r\n",Gloable_u16BinDatePocketNum);
				Gloable_u8MCU_BootSta_last = BootSta_DealFlash;
				HAL_TIM_Base_Stop_IT(&htim8);
				//Show in all pocket date

				/*Flash Oparation Start*/
				HAL_FLASH_Unlock();
				uint32_t sectorError = 0;
				FLASH_EraseInitTypeDef EraseInitStruct;
				EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;   // 按扇区擦除
				EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;  // 电压范围，适用于 2.7V - 3.6V
				EraseInitStruct.Sector = FLASH_SECTOR_2;               // 要擦除的扇区号
				EraseInitStruct.NbSectors = 7;                         // 要擦除的扇区数量
				if (HAL_FLASHEx_Erase(&EraseInitStruct, &sectorError) != HAL_OK) {
				  // 擦除出错，sectorError 包含了出错的扇区号
				  my_printf(">>>>>Erase MCU Flash ERR!!!\r\n");
				  Error_Handler();
				}
				my_printf(">>>>>Erase MCU Flash Done\r\n");
				uint32_t app_startaddress = MCU_FLASH_APP_StartAddress;
				for (uint32_t i=0;i<Gloable_u16BinDatePocketNum;i++)
				{
					uint8_t read_arr_256byte[256];
					BSP_W25Qx_Read(read_arr_256byte,i*256,256);
					uint32_t flash_write_arr_64word[64];
					memcpy(flash_write_arr_64word, read_arr_256byte, 256);
					for (int i = 0; i < 64; i++)
					{
					    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, app_startaddress, flash_write_arr_64word[i]);
					    app_startaddress += 4;
					}
					my_printf(">>>>>Write MCU Flash pocket %d\r\n",i);
				}
				HAL_FLASH_Lock();
				/*Flash Oparation End*/
				Gloable_u8MCU_BootSta = BootSta_JumpAPP;
			}

			break;
		default:
			//when get into default status because err ,should use the blue LED to show in this err
			HAL_Delay(1000);
			HAL_GPIO_TogglePin(Blue_High_GPIO_Port, Blue_High_Pin);
			break;
	}
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM6)
	{
		//if trigle this IT means uart recive date is over
		//step1:copy the all date len from Cnt to Len
		Gloable_u32MCU_TO_PC_UART_RxLen = Gloable_u32MCU_TO_PC_UART_RxCnt;
		//step2:deal with all rxdate
		if (Gloable_u32MCU_TO_PC_UART_RxLen == Gloable_u8MCU_TO_PC_UART_CMDNum)
		{
			uint8_t staycmd[8] = "StayBoot";
			uint8_t overcmd[8] = "SendOver";
			if (compare_arrays(staycmd, Gloable_u8MCU_TO_PC_UART_RxArr, 8) == 1)
			{
				if (Gloable_u8MCU_BootSta == BootSta_StandBy)
				{
					my_printf("Staynow!");
					Gloable_u8MCU_BootSta = BootSta_StayBoot;
					my_printf("\r\n>>>>>ReceiveDate is CMDDate\r\n");
				}
				else if (Gloable_u8MCU_BootSta == BootSta_StayBoot)
				{
					my_printf("Staynow!");
					Gloable_u8MCU_BootSta = BootSta_StayBoot;
					my_printf("\r\n>>>>>Now is in BootSta_StayBoot\r\n");
				}
				else
				{
					my_printf(">>>>>ReceiveDate is CMDDate but ERR STA\r\n");
				}
			}
			else if(compare_arrays(overcmd, Gloable_u8MCU_TO_PC_UART_RxArr, 8) == 1)
			{
				Gloable_u8MCU_BootSta = BootSta_DealFlash;
			}
			else
			{
				my_printf(">>>>>ReceiveDate is CMDDate but ERR\r\n");
			}

		}
		else if(Gloable_u32MCU_TO_PC_UART_RxLen == Gloable_u16MCU_TO_PC_UART_DATANum)
		{
			if (Gloable_u8MCU_BootSta == BootSta_StayBoot)
			{
				if (CalculateCRC16(Gloable_u8MCU_TO_PC_UART_RxArr,Gloable_u32MCU_TO_PC_UART_RxLen-2) == Gloable_u8MCU_TO_PC_UART_RxArr[Gloable_u32MCU_TO_PC_UART_RxLen-2]*256 + Gloable_u8MCU_TO_PC_UART_RxArr[Gloable_u32MCU_TO_PC_UART_RxLen-1])
				{
					Gloable_u8MCU_BootSta = BootSta_DealDate;
					Gloable_u16BinDatePocketNum ++;
					my_printf(">>>>>ReceiveDate is BinDate\r\n");
				}
				else
				{
					my_printf(">>>>>ReceiveDate is BinDate but ERR CRC\r\n");
				}
			}
			else
			{
				my_printf(">>>>>ReceiveDate is BinDate but ERR STA\r\n");
			}
		}
		else
		{
			my_printf(">>>>>ReceiveDate is not CMD and BinDate\r\n");
		}
		//step3:update the Cnt
		Gloable_u32MCU_TO_PC_UART_RxCnt = 0;
		//step4:Stop the tim6
		HAL_TIM_Base_Stop_IT(&htim6);
	}
	else if (htim->Instance == TIM7)//运行指示灯专用tim
	{
		HAL_GPIO_TogglePin(Green_High_GPIO_Port, Green_High_Pin);
	}
	else if (htim->Instance == TIM8)//StandBy跳转JumpAPP专用tim
	{
		if(Gloable_u8MCU_BootSta == BootSta_StandBy)
		{
			HAL_GPIO_TogglePin(Blue_High_GPIO_Port, Blue_High_Pin);
			Gloable_u8MCU_BootSta = BootSta_JumpAPP;
		}

	}
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//step1:get the byte date and send it to the rxdata arr
	Gloable_u8MCU_TO_PC_UART_RxArr [Gloable_u32MCU_TO_PC_UART_RxCnt] =  Gloable_u8MCU_TO_PC_UART_RxBit;
	//step2:sub the Cnt
	Gloable_u32MCU_TO_PC_UART_RxCnt++;
	//step3:update the tim6
	__HAL_TIM_CLEAR_IT(&htim6, TIM_IT_UPDATE);
	HAL_TIM_Base_Start_IT(&htim6);
	//step4:update the uart RxIT
	HAL_UART_Receive_IT(&MCU_TO_PC_UART_port, (uint8_t *)&Gloable_u8MCU_TO_PC_UART_RxBit, 1);
}

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
